package com.mhy.logcat;


import android.text.TextUtils;
import android.util.Log;
import android.util.LogPrinter;
import android.util.Printer;

import java.io.File;
import java.util.Date;
import java.util.Locale;

/**
 * Created By Mahongyin
 * Date    2025/1/20 17:59
 * LogCat
 * enablePrintLog() 允许打印Log
 * enableSendLog() 允许发送Log
 * setSendLog()
 * setFileLog()
 */
public class LogCat {
    private LogCat() {
    }

    private static String className = "";
    private static String methodName = "";
    private static int lineNumber = 0;
    /**
     * 允许打印log  默认不允许
     */
    private static boolean enablePrintLog = true;
    /**
     * 允许保存文件或udp发送log  默认不允许
     */
    static boolean enableSendLog = false;
    static Printer printer = null;

    /**
     * 最大一次打印长度
     */
    private final static int MAX_LENGTH = 2000;

    private static final String start =
            "[开始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>||\n";

    private static final String end =
            "||<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<结束]\n";

    /**
     * 设置 允许打印Log true 打印日志
     */
    public static void enableLogPrint(boolean print) {
        enablePrintLog = print;
    }

    /**
     * 允许发送日志
     */
    public static void enableLogSend(boolean send) {
        enableSendLog = send;
    }

    /**
     * 网络/文件2选1
     * udp 日志 设置udpIp和udpPort 发送日志将拦截Logcat打印日志, 适合动态打开发送日志，取消则把udpIp/udpPort置空即可
     */
    public static void setSendLog(String udpIp, int udpPort) {//9696
        if (TextUtils.isEmpty(udpIp) || udpPort == 0) {
            return;
        }
        LogManager.setTarget(udpIp, udpPort);
    }

    /**网络/文件2选1
     * @param file   保存文件, 取消则把file置空即可
     * @param append true追加？  false覆盖 只留最后一条？
     */
    public static void setFileLog(File file, boolean append) {//9696
        if (file == null) {
            return;
        }
        LogManager.setTarget(file, append);
    }

    public static void setFileLog(File file) {//9696
        if (file == null) {
            return;
        }
        LogManager.setTarget(file, true);
    }

    /***************************************************************************************/

    public static void v(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.VERBOSE, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.VERBOSE, className, String.valueOf(msg));
        }
    }

    public void v(Throwable t) {
        if (enableSendLog && printer != null) {
            generateLog(Log.VERBOSE, generateTag(), getStackTraceString(t));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.VERBOSE, className, getStackTraceString(t));
        }
    }

    public static void v(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.VERBOSE, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.VERBOSE, tag + "|" + className, String.valueOf(msg));
        }
    }

    public static void d(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.DEBUG, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.DEBUG, className, String.valueOf(msg));
        }
    }

    public static void d(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.DEBUG, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.DEBUG, tag, String.valueOf(msg));
        }
    }

    public static void d(Throwable th) {
        if (enableSendLog && printer != null) {
            generateLog(Log.DEBUG, generateTag(), getStackTraceString(th));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.DEBUG, className, getStackTraceString(th));
        }
    }

    public static void i(Throwable th) {
        if (enableSendLog && printer != null) {
            generateLog(Log.INFO, generateTag(), getStackTraceString(th));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.INFO, className, getStackTraceString(th));
        }
    }

    public static void i(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.INFO, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.INFO, className, String.valueOf(msg));
        }
    }

    public static void i(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.INFO, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.INFO, tag, String.valueOf(msg));
        }
    }

    public static void w(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.WARN, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.WARN, className, String.valueOf(msg));
        }
    }

    public static void w(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.WARN, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.WARN, tag, String.valueOf(msg));
        }
    }

    public static void w(Throwable th) {
        if (enableSendLog && printer != null) {
            generateLog(Log.WARN, generateTag(), getStackTraceString(th));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.WARN, className, getStackTraceString(th));
        }
    }

    public static void e(Throwable t) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ERROR, generateTag(), getStackTraceString(t));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ERROR, className, getStackTraceString(t));
        }
    }

    public static void e(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ERROR, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ERROR, className, String.valueOf(msg));
        }
    }

    public static void e(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ERROR, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ERROR, tag, String.valueOf(msg));
        }
    }

    public static void a(Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ASSERT, generateTag(), String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ASSERT, className, String.valueOf(msg));
        }
    }

    public static void a(String tag, Object msg) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ASSERT, tag, String.valueOf(msg));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ASSERT, tag, String.valueOf(msg));
        }
    }

    public static void a(Throwable th) {
        if (enableSendLog && printer != null) {
            generateLog(Log.ASSERT, generateTag(), getStackTraceString(th));
        }
        if (enablePrintLog) {
            getStackTraceElement();
            maxPrint(Log.ASSERT, className, getStackTraceString(th));
        }
    }

    /***************************************************************************************/

    public static void print(int leave, String tag, String msg) {
        switch (leave) {
            case Log.DEBUG:
                Log.d(tag, msg);
                break;
            case Log.INFO:
                Log.i(tag, msg);
                break;
            case Log.WARN:
                Log.w(tag, msg);
                break;
            case Log.ERROR:
                Log.e(tag, msg);
                break;
            case Log.VERBOSE:
                Log.v(tag, msg);
                break;
            default:
                break;
        }
    }

    /**
     * 适应最大长度打印
     *
     * @param tag 标志
     * @param msg 信息
     */
    private static void maxPrint(int leave, String tag, String msg) {
        if (null == msg) {
            return;
        }
        print(leave, tag, start);
        if (msg.length() > MAX_LENGTH) {
            int length = MAX_LENGTH + 1;
            String remain = msg;
            int index = 0;
            while (length > MAX_LENGTH) {
                index++;
                print(leave, tag + "[" + index + "]", createLog(remain.substring(0, MAX_LENGTH)));
                remain = remain.substring(MAX_LENGTH);
                length = remain.length();
            }
            // 不足max长度部分
            if (length <= MAX_LENGTH) {
                index++;
                print(leave, tag + "[" + index + "]", createLog(remain));
            }
        } else {
            print(leave, tag, createLog(msg));
        }
        print(leave, tag, end);
    }

    private static String createLog(String msg) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("(");
        buffer.append(className);
        buffer.append(":");
        buffer.append(lineNumber);
        buffer.append(")");
        buffer.append("[");
        buffer.append(methodName);
        buffer.append("]:");
        buffer.append(msg);
        return buffer.toString();
    }

    /**
     * 获取堆栈,定位层级
     */
    private static void getStackTraceElement() {
        StackTraceElement[] sElements = Thread.currentThread().getStackTrace();
//        String stack = Arrays.toString(sElements);
//        Log.e("mhy", stack);
        StackTraceElement sElement = sElements[4];
        className = sElement.getFileName();
        methodName = sElement.getMethodName();
        lineNumber = sElement.getLineNumber();
        //className = generateTag();
    }

    public static String getStackTraceString(Throwable t) {
        return Log.getStackTraceString(t);
    }

    /***************************************************************************************/

    //根据堆栈生成TAG:  TAG|className.methodName(fileName:lineNumber)
    private static String generateTag() {
        StackTraceElement[] stackTraceElement = Thread.currentThread().getStackTrace();
//        String stack = Arrays.toString(stackTraceElement);
//        Log.e("mhy", stack);
        StackTraceElement caller = stackTraceElement[4];
        String reg = "%s.%s(%s:%d)";
        String callerClazzName = caller.getClassName();
        callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
        String tag = String.format(Locale.getDefault(), reg, callerClazzName, caller.getMethodName(), caller.getFileName(), caller.getLineNumber());
        StringBuilder sb = new StringBuilder();
        //sb.append("|");
        sb.append(tag);
        return sb.toString();
    }

    private static void generateLog(int priority, String tag, String msg) {
        String level;
        switch (priority) {
            case Log.VERBOSE:
                level = "V";
                break;
            case Log.DEBUG:
                level = "D";
                break;
            case Log.INFO:
                level = "I";
                break;
            case Log.WARN:
                level = "W";
                break;
            case Log.ERROR:
                level = "E";
                break;
            case Log.ASSERT:
                level = "A";
                break;
            default:
                level = String.valueOf(priority);
                break;
        }
        Date date = new Date();
        String str = String.format(Locale.getDefault(), printer instanceof LogPrinter ? "%s %2$tY-%tm-%<td %<tT.%<tL %s %s" : "%s\t%2$tY-%tm-%<td %<tT.%<tL\t%s\t%s", new Object[]{level, date, tag, msg});
        if (enableSendLog) {
            printer.println(str);
        }
        //return str.length() + 1;
    }

}
